﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ServerSide
{
    #region Using

    using DevComponents.DotNetBar;
    using System.Net.Sockets;
    using System.Net;
    
    #endregion

    public partial class FrmMain : Office2007Form
    {
        #region Variables

        /// <summary>
        /// Socket to receive and send data
        /// </summary>
        private AsyncSocket m_AsynSocket = new AsyncSocket();

        /// <summary>
        /// Data source of gridview players
        /// </summary>
        private DataTable m_tbPlayer;

        /// <summary>
        /// Delegate to append status
        /// </summary>
        /// <param name="data"></param>
        private delegate void AppendStatusDel(string data);

        /// <summary>
        /// Delegate to start game
        /// </summary>
        private delegate void StartGameDel();

        #endregion


        public FrmMain()
        {
            InitializeComponent();
            m_AsynSocket.Handler += HandleSocketEvent;
            txtServerStatus.AppendText("Waiting for Start" + Environment.NewLine);
            txtServerAddress.Text = this.GetIP();
        }

        /// <summary>
        /// When AsyncSocket object raises event handler, this routine will be called
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void HandleSocketEvent(object sender, EventArgs e)
        {
            var dataTransfer = (SocketEvent)e;
            var compData = dataTransfer.Data.Split(new char[] { ':' });
            switch (compData[0])
            {
                case "ERROR":
                    MessageBox.Show(compData[1], "Error", MessageBoxButtons.OK);
                    break;

                case "NOTIFY":
                    var comp2 = compData[1].Split(new char[] { '#' });
                    if (comp2[0].Equals("CONNECT"))
                    {
                        m_tbPlayer.Rows.Add(comp2[1], 0, "Playing");
                        WriteStatus(comp2[2]);
                    }
                    else if (comp2[0].Equals("DISCONNECT"))
                    {
                        RemovePlayer(comp2[1]);
                        WriteStatus(comp2[2]);
                    }
                    else
                    {
                        WriteStatus(compData[1]);
                    }
                    break;

                case "RESULT":
                    var compPlayers = compData[1].Split(new char[] { '@' });
                    if (compPlayers.Length == 2)
                    {
                        var compResult = compPlayers[0].Split(new char[] { '#' });
                        var playerName1 = compResult[0];
                        var playerResult1 = compResult[1];

                        compResult = compPlayers[1].Split(new char[] { '#' });
                        var playerName2 = compResult[0];
                        var playerResult2 = compResult[1];

                        // Player 1 win player 2
                        var result1 = 0;
                        var result2 = 0;
                        DataRow row1 = RetrievePlayer(playerName1);
                        DataRow row2 = RetrievePlayer(playerName2);

                        if (CompareResult(playerResult1, playerResult2) == 1)
                        {
                            result1 = 1;
                            result2 = -1;
                            row1["Score"] = int.Parse(row1["Score"].ToString()) + 1;
                        }
                        else if ((result2 = CompareResult(playerResult1, playerResult2)) == -1)
                        {
                            result1 = -1;
                            result2 = 1;
                            row2["Score"] = int.Parse(row2["Score"].ToString()) + 1;
                        }
                        this.m_AsynSocket.SendResult(playerName1, result1, int.Parse(row1["Score"].ToString()), playerResult2);
                        this.m_AsynSocket.SendResult(playerName2, result2, int.Parse(row2["Score"].ToString()), playerResult1);
                        this.m_AsynSocket.DictPlayerResult.Clear();
                    }
                    break;
            }
        }

        /// <summary>
        /// Compare result of 2 players
        /// </summary>
        /// <param name="result1"></param>
        /// <param name="result2"></param>
        /// <returns></returns>
        private int CompareResult(string result1, string result2)
        {
            if (result1 == "ROCK" && result2 == "SCISSORS")
            {
                return 1;
            }
            if (result1 == "ROCK" && result2 == "PAPER")
            {
                return -1;
            }
            if (result1 == "ROCK" && result2 == "ROCK")
            {
                return 0;
            }

            if (result1 == "PAPER" && result2 == "SCISSORS")
            {
                return -1;
            }
            if (result1 == "PAPER" && result2 == "PAPER")
            {
                return 0;
            }
            if (result1 == "PAPER" && result2 == "ROCK")
            {
                return 1;
            }

            if (result1 == "SCISSORS" && result2 == "SCISSORS")
            {
                return 0;
            }
            if (result1 == "SCISSORS" && result2 == "PAPER")
            {
                return 1;
            }
            if (result1 == "SCISSORS" && result2 == "ROCK")
            {
                return -1;
            }
            return 0;
        }
        /// <summary>
        /// Get IP Address of this server
        /// </summary>
        /// <returns></returns>
        private string GetIP()
        {
            IPHostEntry hostByName = Dns.GetHostByName(Dns.GetHostName());
            IPAddress[] addressList = hostByName.AddressList;
            int index = 0;
            while (index < addressList.Length)
            {
                IPAddress address = addressList[index];
                return address.ToString();
            }
            return string.Empty;
        }

        /// <summary>
        /// Validate input by user such as port and server address
        /// </summary>
        /// <returns>true: valid and false: invalid</returns>
        private bool ValidateInputs(ref string errTxt)
        {
            if (txtServerAddress.Text == string.Empty)
            {
                errTxt = "Server address is empty";
                return false;
            }
            if (txtServerPort.Text == string.Empty)
            {
                errTxt = "Server port is empty";
                return false;
            }
            int n;
            if (!int.TryParse(txtServerPort.Text, out n))
            {
                errTxt = "Server port must be numeric";
                return false;
            }
            return true;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            var errTxt = string.Empty;
            if (!ValidateInputs(ref errTxt))
            {
                MessageBox.Show(errTxt, "Error", MessageBoxButtons.OK);
                return;
            }
            try
            {
                if (this.m_AsynSocket.Listen(txtServerAddress.Text.Trim(), txtServerPort.Text.Trim()))
                {
                    this.UpdateControls(true);
                }
            }
            catch (SocketException se)
            {
                Common.Logger.Error(se.Message);
                MessageBox.Show(se.Message, "Error", MessageBoxButtons.OK);
            }
        }

        private void WriteStatus(string data)
        {
            if (txtServerStatus.InvokeRequired)
            {
                AppendStatusDel del = new AppendStatusDel(WriteStatus);
                txtServerStatus.Invoke(del, new object[] { data });
                return;
            }
            txtServerStatus.AppendText(data + Environment.NewLine);
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            this.m_AsynSocket.CloseSockets();
            this.UpdateControls(false);
            txtServerStatus.AppendText("Server stopped" + Environment.NewLine);
            m_tbPlayer.Rows.Clear();
        }

        private void UpdateControls(bool listening)
        {
            this.btnStart.Enabled = !listening;
            this.btnStop.Enabled = listening;
        }

        private void FrmMain_Load(object sender, EventArgs e)
        {
            InitGridView();
            InitDataTable();
        }

        /// <summary>
        /// Init data gridview players
        /// </summary>
        private void InitGridView()
        {
            DataGridViewTextBoxColumn objColumn = new DataGridViewTextBoxColumn
            {
                HeaderText = "Player",
                DataPropertyName = "Player",
                Name = "Player",
                Width = (int)((dtgvPlayers.Width - dtgvPlayers.RowHeadersWidth) * ((double)1 / 3))
            };
            dtgvPlayers.Columns.Add(objColumn);

            objColumn = new DataGridViewTextBoxColumn
            {
                HeaderText = "Score",
                DataPropertyName = "Score",
                Name = "Score",
                Width = (int)((dtgvPlayers.Width - dtgvPlayers.RowHeadersWidth) * ((double)1 / 3))
            };
            dtgvPlayers.Columns.Add(objColumn);

            objColumn = new DataGridViewTextBoxColumn
            {
                HeaderText = "Status",
                DataPropertyName = "Status",
                Name = "Status",
                Width = (int)((dtgvPlayers.Width - dtgvPlayers.RowHeadersWidth) * ((double)1 / 3)) - 1
            };
            dtgvPlayers.Columns.Add(objColumn);
        }

        /// <summary>
        /// Init data source for gridview players
        /// </summary>
        private void InitDataTable()
        {
            m_tbPlayer = new DataTable();
            m_tbPlayer.Columns.Add("Player");
            m_tbPlayer.Columns.Add("Score");
            m_tbPlayer.Columns.Add("Status");

            DataView objTimeSheetDV = new System.Data.DataView(m_tbPlayer);
            objTimeSheetDV.AllowNew = false;
            dtgvPlayers.AutoGenerateColumns = false;
            dtgvPlayers.DataSource = objTimeSheetDV;
            dtgvPlayers.ClearSelection();
        }

        private void RemovePlayer(string playerName)
        {
            foreach (DataRow row in m_tbPlayer.Rows)
            {
                if (row["Player"].ToString().ToUpper().Equals(playerName.ToUpper()))
                {
                    m_tbPlayer.Rows.Remove(row);
                    break;
                }
            }
        }

        private DataRow RetrievePlayer(string playerName)
        {
            foreach (DataRow row in m_tbPlayer.Rows)
            {
                if (row["Player"].ToString().ToUpper().Equals(playerName.ToUpper()))
                {
                    return row;
                }
            }
            return null;
        }
        private void FrmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.m_AsynSocket.CloseSockets();
            this.UpdateControls(false);
            txtServerStatus.AppendText("Server stopped" + Environment.NewLine);
            m_tbPlayer.Rows.Clear();
        }
    }
}
